package com.pingan.haofang.searchcloud.web.index.validator;

import com.google.common.collect.Sets;
import com.pingan.haofang.framework.common.functional.lang.control.base.Enums;
import com.pingan.haofang.framework.common.functional.lang.control.validator.bo.ValidationError;
import com.pingan.haofang.framework.common.functional.lang.control.validator.bo.ValidationResult;
import com.pingan.haofang.searchcloud.api.constants.EngineType;
import com.pingan.haofang.searchcloud.api.matedata.constant.MetadataFieldType;
import com.pingan.haofang.searchcloud.common.configuration.ClusterProperties;
import com.pingan.haofang.searchcloud.common.validator.annotation.ValidHandler;
import com.pingan.haofang.searchcloud.index.service.ClusterService;
import com.pingan.haofang.searchcloud.web.index.form.IndexForm;
import com.pingan.haofang.searchcloud.web.index.form.IndexInfoForm;
import com.pingan.haofang.searchcloud.web.metadata.form.MetadataFieldForm;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Index Controller相关表单Validator
 *
 * @author baojie371
 * @date 17/5/5
 * @since 1.0.0
 */
@Component
public class IndexValidator {

    @Autowired
    private ClusterService clusterService;

    private final static Set<String> keywords = Sets.newHashSet("ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC",
            "ASENSITIVE", "BEFORE", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", "CALL", "CASCADE", "CASE",
            "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", "COLUMN", "CONDITION", "CONNECTION", "CONSTRAINT",
            "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
            "CURRENT_USER", "CURSOR", "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE",
            "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE",
            "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", "ELSEIF",
            "ENCLOSED", "ESCAPED", "EXISTS", "EXIT", "EXPLAIN", "FALSE", "FETCH", "FLOAT", "FLOAT4", "FLOAT8", "FOR",
            "FORCE", "FOREIGN", "FROM", "FULLTEXT", "GOTO", "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY",
            "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IN", "INDEX", "INFILE", "INNER", "INOUT",
            "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IS",
            "ITERATE", "JOIN", "KEY", "KEYS", "KILL", "LABEL", "LEADING", "LEAVE", "LEFT", "LIKE", "LIMIT", "LINEAR",
            "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP",
            "LOW_PRIORITY", "MATCH", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND",
            "MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT", "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON",
            "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", "PRECISION", "PRIMARY",
            "PROCEDURE", "PURGE", "RAID0", "RANGE", "READ", "READS", "REAL", "REFERENCES", "REGEXP", "RELEASE",
            "RENAME", "REPEAT", "REPLACE", "REQUIRE", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "SCHEMA",
            "SCHEMAS", "SECOND_MICROSECOND", "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SMALLINT", "SPATIAL",
            "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS",
            "SQL_SMALL_RESULT", "SSL", "STARTING", "STRAIGHT_JOIN", "TABLE", "TERMINATED", "THEN", "TINYBLOB",
            "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED",
            "UPDATE", "USAGE", "USE", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY",
            "VARCHAR", "VARCHARACTER", "VARYING", "WHEN", "WHERE", "WHILE", "WITH", "WRITE", "X509", "XOR",
            "YEAR_MONTH", "ZEROFILL");

    @ValidHandler
    public void save(ValidationResult result, IndexInfoForm form) {

        IndexForm basic = form.getBasicInfo();

        // indexName不能为关键字
        String indexName = basic.getIndexName();
        if (keywords.contains(indexName.toUpperCase())) {
            result.addError(ValidationError.of("basicInfo.indexName",
                    "index.indexName.not.allow",
                    indexName)
                    .invalidValue(indexName));
            return;
        }

        // 校验集群
        EngineType engineType = Enums.findValue(basic.getEngineType(), EngineType.class);
        Map<String, ClusterProperties.ClusterInfo> clusterInfoMap =
                clusterService.getClusterMap(engineType);
        if (!clusterInfoMap.containsKey(form.getBasicInfo().getClusterName())) {
            result.addError(ValidationError.of("basicInfo.clusterName",
                    "index.clusterName.illegal"));
            return;
        }

        // 校验权限(若配置的权限，但是不)
        ClusterProperties.ClusterInfo clusterInfo = clusterInfoMap.get(form.getBasicInfo().getClusterName());
        if (CollectionUtils.isNotEmpty(clusterInfo.getAllowProjects())
                && !clusterInfo.getAllowProjects().contains(form.getBasicInfo().getProjectId())) {
            result.addGlobalError("index.clusterName.not.allowed");
            return;
        }


        List<MetadataFieldForm> fields = form.getFields();
        Set<String> fieldSet = new HashSet<>(fields.size());
        Set<String> versionSet = new HashSet<>(fields.size());
        boolean hasUniqueKey = false;
        for (int i = 0; i < fields.size(); i++) {
            MetadataFieldForm field = fields.get(i);

            // 字段名不能重复，不能为关键字
            String fieldName = field.getFieldName();
            if (fieldSet.contains(fieldName)) {
                result.addError(ValidationError.of(String.format("fields[%d].fieldName", i),
                        "metadata.field.fieldName.illegal")
                        .invalidValue(fieldName));
                return;
            }

            if (keywords.contains(fieldName.toUpperCase())) {
                result.addError(ValidationError.of(String.format("fields[%d].fieldName", i)
                        , "metadata.field.fieldName.not.allow", fieldName)
                        .invalidValue(fieldName));
                return;
            }
            fieldSet.add(fieldName);
            if (field.getVersionKey()) {
                versionSet.add(field.getFieldName());
            }

            if(field.getVersionKey()
                    && field.getFieldTypeId() != MetadataFieldType.INT.getValue()
                    && field.getFieldTypeId() != MetadataFieldType.LONG.getValue()){
                result.addError(ValidationError.of(String.format("fields[%d].fieldName", i),
                        "metadata.field.versionKey.not.allow",fieldName).invalidValue(fieldName));
                return;
            }


            // defaultValue校验
            String defaultValue = field.getDefaultValue();

            if (StringUtils.isNotEmpty(defaultValue)) {
                if (field.getUniqueKey() || field.getRequired()) {
                    result.addError(ValidationError.of(String.format("fields[%d].defaultValue", i),
                            "metadata.field.defaultValue.illegal")
                            .invalidValue(field.getDefaultValue()));
                }

                // todo defaultValue值校验
            }

            // 唯一键最多只有一个
            boolean uniqueKey = field.getUniqueKey();
            if (uniqueKey) {
                if (hasUniqueKey) {
                    result.addError(ValidationError.of(String.format("fields[%d].uniqueKey", i),
                            "metadata.field.uniqueKey.illegal")
                            .invalidValue(uniqueKey));
                    return;
                } else {
                    hasUniqueKey = true;
                }
            }
        }

        // 唯一键至少只有一个
        if (!hasUniqueKey) {
            result.addGlobalError("metadata.field.uniqueKey.must.have");
            return;
        }
        if(versionSet.size()!=1){
            result.addGlobalError("metadata.field.versionKey.must.haveone");
            return;
        }

    }
}
